package com.cskaoyan.wordmemorize.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cskaoyan.wordmemorize.common.constant.ResultCodeEnum;
import com.cskaoyan.wordmemorize.common.exception.BusinessException;
import com.cskaoyan.wordmemorize.converter.ReviewConverter;
import com.cskaoyan.wordmemorize.dao.entity.*;
import com.cskaoyan.wordmemorize.dao.mapper.*;
import com.cskaoyan.wordmemorize.dto.admin.PageDTO;
import com.cskaoyan.wordmemorize.dto.app.UserCheckinDTO;
import com.cskaoyan.wordmemorize.dto.app.UserCheckinStatisticsDTO;
import com.cskaoyan.wordmemorize.request.PageRequest;
import com.cskaoyan.wordmemorize.request.UserCheckinCommand;
import com.cskaoyan.wordmemorize.service.UserCheckinService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List;

@Service
public class UserCheckinServiceImpl implements UserCheckinService {

    @Autowired
    UserMapper userMapper;

    @Autowired
    LevelMapper levelMapper;

    @Autowired
    UserCheckinMapper userCheckinMapper;

    @Autowired
    UserCheckinStatisticsMapper userCheckinStatisticsMapper;

    @Autowired
    UserVocBoundStatisticsMapper userVocBoundStatisticsMapper;

    @Autowired
    UserVocBoundLogMapper userVocBoundLogMapper;

    @Autowired
    ReviewConverter reviewConverter;


    @Override
    public PageDTO<UserCheckinDTO> getCheckInData(PageRequest pageRequest, Long userId) {
        LambdaQueryWrapper<UserCheckinDO> checkinDOLambdaQueryWrapper = new LambdaQueryWrapper<>();
        checkinDOLambdaQueryWrapper.eq(UserCheckinDO::getUserId, userId);

        PageDTO<UserCheckinDTO> userCheckinDTOPageDTO = null;

        // 没有分页参数
        if (pageRequest.getPageNum() == null || pageRequest.getCount() == null || pageRequest.getCount() == 0) {
            List<UserCheckinDO> userCheckinDOS = userCheckinMapper.selectList(checkinDOLambdaQueryWrapper);
            userCheckinDTOPageDTO = reviewConverter.userCheckinPage2PageDTO(userCheckinDOS, (long) userCheckinDOS.size());

        } else {
            Page<UserCheckinDO> page = new Page<>(pageRequest.getPageNum(), pageRequest.getCount());
            Page<UserCheckinDO> userCheckinDOPage = userCheckinMapper.selectPage(page, checkinDOLambdaQueryWrapper);
            userCheckinDTOPageDTO = reviewConverter.userCheckinPage2PageDTO(userCheckinDOPage.getRecords(), userCheckinDOPage.getTotal());
        }

        return userCheckinDTOPageDTO;
    }

    @Transactional
    @Override
    public UserCheckinStatisticsDTO checkin(UserCheckinCommand command, Long userId) {
        // 检查用户当天是否签到

        // 从用户表中获取数据
        LambdaQueryWrapper<UserDO> userDOQueryWrapper = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<UserDO> eq = userDOQueryWrapper.eq(UserDO::getId, userId);
        UserDO userDO = userMapper.selectOne(eq);

        // 设置command的userId
        command.setUserId(String.valueOf(userId));

        // 拿最近的签到数据
        QueryWrapper<UserCheckinDO> checkinDOQueryWrapper = new QueryWrapper<>();
        checkinDOQueryWrapper.eq("user_id", userId)
                .orderByDesc("sign_date")
                .last("limit 1");
        UserCheckinDO recentUserCheckinDO = userCheckinMapper.selectOne(checkinDOQueryWrapper);

        // 用户签到统计数据
        LambdaQueryWrapper<UserCheckinStatisticsDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserCheckinStatisticsDO::getUserId, userId);
        UserCheckinStatisticsDO checkinStatisticsDO = userCheckinStatisticsMapper.selectOne(lambdaQueryWrapper);

        // 更新签到统计数据的条件
        LambdaUpdateWrapper<UserCheckinStatisticsDO> userCheckinStatisticsDOLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userCheckinStatisticsDOLambdaUpdateWrapper.eq(UserCheckinStatisticsDO::getUserId, userId);

        // 用户第一次签到(刚注册), 没有以往的签到数据
        if (recentUserCheckinDO == null) {
            if (checkinStatisticsDO.getTotalDays() == 0) {
                // 更新签到统计数据
                checkinStatisticsDO.setTotalDays(1);
                checkinStatisticsDO.setMaxContinuousDays(1);
                checkinStatisticsDO.setCurContinuousDays(1);
                checkinStatisticsDO.setCurContinuousStartDate(LocalDate.now());
                checkinStatisticsDO.setMaxContinuousStartDate(LocalDate.now());
            } else {
                checkinStatisticsDO.setTotalDays(checkinStatisticsDO.getTotalDays() + 1);
                checkinStatisticsDO.setCurContinuousDays(checkinStatisticsDO.getCurContinuousDays() + 1);
                if (checkinStatisticsDO.getCurContinuousDays() > checkinStatisticsDO.getMaxContinuousDays()) {
                    checkinStatisticsDO.setMaxContinuousStartDate(checkinStatisticsDO.getCurContinuousStartDate());
                    checkinStatisticsDO.setMaxContinuousDays(checkinStatisticsDO.getCurContinuousDays());
                }
            }
            userCheckinStatisticsMapper.update(checkinStatisticsDO, userCheckinStatisticsDOLambdaUpdateWrapper);

            // 插入签到记录 并 更新段号
            UserCheckinDO userCheckinDO = new UserCheckinDO();
            userCheckinDO.setUserId(userId);
            userCheckinDO.setSignDate(command.getSignDate());
            userCheckinDO.setType(command.getType());
            updateUserCheckinStickNo(userCheckinDO);

            // 更新单词上限和日志
            vocUpdateAndLog(userId);

            // 更新用户等级
            userLevelUpdate(userId, checkinStatisticsDO);

            return reviewConverter.userCheckinStatisticsDO2DTO(checkinStatisticsDO);
        }

        // 当前日期
        LocalDate today = LocalDate.now();
        // 前一天日期
        LocalDate yesterday = today.minusDays(1);
        // 判断今天是否签到
        boolean isCheckin = recentUserCheckinDO.getSignDate().isEqual(today);

        // 今天已签到
        if (isCheckin) {
            throw new BusinessException(ResultCodeEnum.ALREADY_CHECKIN);
        }

        // 今天没有签到
        UserCheckinDO nowUserCheckinDO = reviewConverter.userCheckinCommand2DO(command);

        // 总签到天数 + 1
        checkinStatisticsDO.setTotalDays(checkinStatisticsDO.getTotalDays() + 1);

        // 判断昨天是否签到，确认连续签到段号
        if (recentUserCheckinDO.getSignDate().isEqual(yesterday)) {
            // 使用昨日连续签到段号
            nowUserCheckinDO.setStickNo(recentUserCheckinDO.getStickNo());

            // 变更签到统计数据的当前最大连续签到天数 + 1
            checkinStatisticsDO.setCurContinuousDays(checkinStatisticsDO.getCurContinuousDays() + 1);

            // 如果 当前最大连续签到天数 大于 最大连续签到天数， 更新最大连续签到起始日期
            if (checkinStatisticsDO.getCurContinuousDays() > checkinStatisticsDO.getMaxContinuousDays()) {
                checkinStatisticsDO.setMaxContinuousStartDate(checkinStatisticsDO.getCurContinuousStartDate());
                checkinStatisticsDO.setMaxContinuousDays(checkinStatisticsDO.getCurContinuousDays());
            }

            // 插入签到数据
            userCheckinMapper.insert(nowUserCheckinDO);
        } else {
            // 使用新的连续签到段号
            updateUserCheckinStickNo(nowUserCheckinDO);

            // 变更连续签到起始日期和连续签到天数
            checkinStatisticsDO.setCurContinuousDays(1);
            checkinStatisticsDO.setCurContinuousStartDate(LocalDate.now());
        }
        // 插入签到数据
        //userCheckinMapper.insert(nowUserCheckinDO);

        // 更新签到统计数据
        userCheckinStatisticsMapper.update(checkinStatisticsDO, userCheckinStatisticsDOLambdaUpdateWrapper);

        // 更新单词上限和日志
        vocUpdateAndLog(userId);

        // 更新用户等级
        userLevelUpdate(userId, checkinStatisticsDO);


        return reviewConverter.userCheckinStatisticsDO2DTO(checkinStatisticsDO);
    }

    // 插入签到记录 并 更新段号
    private void updateUserCheckinStickNo(UserCheckinDO nowUserCheckinDO) {
        // 插入
        userCheckinMapper.insert(nowUserCheckinDO);

        // 取出，拿到id
        LambdaQueryWrapper<UserCheckinDO> checkinDOLambdaQueryWrapper = new LambdaQueryWrapper<>();
        checkinDOLambdaQueryWrapper.eq(UserCheckinDO::getSignDate, nowUserCheckinDO.getSignDate())
                .eq(UserCheckinDO::getUserId, nowUserCheckinDO.getUserId());
        UserCheckinDO userCheckinDO = userCheckinMapper.selectOne(checkinDOLambdaQueryWrapper);

        // 更新连续段号设置为id
        LambdaUpdateWrapper<UserCheckinDO> userCheckinDOLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userCheckinDOLambdaUpdateWrapper.eq(UserCheckinDO::getSignDate, userCheckinDO.getSignDate())
                .eq(UserCheckinDO::getUserId, nowUserCheckinDO.getUserId())
                .set(UserCheckinDO::getStickNo, userCheckinDO.getId());
        userCheckinMapper.update(userCheckinDOLambdaUpdateWrapper);
    }

    // 更新单词上限和日志
    private void vocUpdateAndLog(Long userId) {
        // 更新单词统计
        LambdaUpdateWrapper<UserVocBoundStatisticsDO> vocBoundStatisticsDOUpdateWrapper = new LambdaUpdateWrapper<>();
        vocBoundStatisticsDOUpdateWrapper.eq(UserVocBoundStatisticsDO::getUserId, userId)
                .setSql("free = free + 1")
                .setSql("total = total + 1")
                .setSql("available = available + 1");
        userVocBoundStatisticsMapper.update(vocBoundStatisticsDOUpdateWrapper);

        // 添加单词上限变化日志
        UserVocBoundLogDO userVocBoundLogDO = new UserVocBoundLogDO();
        userVocBoundLogDO.setUserId(userId);
        userVocBoundLogDO.setType(2);
        userVocBoundLogDO.setLogDate(LocalDate.now());
        userVocBoundLogDO.setCount(1);
        userVocBoundLogDO.setDescription("签到奖励");
        userVocBoundLogMapper.insert(userVocBoundLogDO);
    }

    // 更新用户等级
    private void userLevelUpdate(Long userId, UserCheckinStatisticsDO checkinStatisticsDO) {
        // 更新等级

        // 从用户表中获取数据
        LambdaQueryWrapper<UserDO> userDOQueryWrapper = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<UserDO> eq = userDOQueryWrapper.eq(UserDO::getId, userId);
        UserDO userDO = userMapper.selectOne(eq);

        // 更新用户信息条件
        LambdaUpdateWrapper<UserDO> userDOLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userDOLambdaUpdateWrapper.eq(UserDO::getId, userId);

        // 用户当前等级
        Integer nowLevelValue = userDO.getLevelValue();
        // 用户当前等级升级日期
        LocalDate nowLevelDate = userDO.getLevelDate();

        // 最高等级，不再升级
        if (nowLevelValue >= 12) return;

        // 获取当前等级对应的签到天数
        LambdaQueryWrapper<LevelDO> levelDOQueryWrapper = new LambdaQueryWrapper<>();
        levelDOQueryWrapper.eq(LevelDO::getLevelValue, nowLevelValue);
        LevelDO levelDO = levelMapper.selectOne(levelDOQueryWrapper);
        Integer checkInDays = levelDO.getCheckInDays();

        // 获取当前等级升级日期之后的签到天数
        LambdaQueryWrapper<UserCheckinDO> checkinDOLambdaQueryWrapper = new LambdaQueryWrapper<>();
        checkinDOLambdaQueryWrapper.eq(UserCheckinDO::getUserId, userId)
                .gt(UserCheckinDO::getSignDate, nowLevelDate);
        List<UserCheckinDO> userCheckinDOS = userCheckinMapper.selectList(checkinDOLambdaQueryWrapper);
        int days = userCheckinDOS.size();

        // 拿用户等级升级当天的签到记录，如果有，天数得 + 1，没有就不加
        LambdaQueryWrapper<UserCheckinDO> userCheckinDOLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userCheckinDOLambdaQueryWrapper.eq(UserCheckinDO::getUserId, userId)
                .eq(UserCheckinDO::getSignDate, nowLevelDate);
        UserCheckinDO isUserCheckinDO = userCheckinMapper.selectOne(userCheckinDOLambdaQueryWrapper);
        // 因为如果今天买等级了，等级更新日期为今天，但同时今天签到，此时不能从等级更新日期之后算签到日期
        // 所以如果今天还没签到，签到天数得 + 1
        if (nowLevelValue < 6) {
            if (isUserCheckinDO == null) { // 新用户，没有签到记录，但是等级更新日期为注册那天
                days += 1;
            }
        }

        // 下一级的数据
        LambdaQueryWrapper<LevelDO> levelerDOQueryWrapper = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<LevelDO> levelDOLambdaQueryWrapper = levelerDOQueryWrapper.eq(LevelDO::getLevelValue, nowLevelValue + 1);
        LevelDO nextLevelDO = levelMapper.selectOne(levelDOLambdaQueryWrapper);


        // 判断是否升级
        if (nowLevelValue < 5) {
            // 满足升级条件
            // (checkinStatisticsDO.getTotalDays() >= nextLevelDO.getCheckInDays())用于方便测试
            if (((checkInDays + days) >= nextLevelDO.getCheckInDays()) || (checkinStatisticsDO.getTotalDays() >= nextLevelDO.getCheckInDays())) {
                //if (checkinStatisticsDO.getTotalDays() >= nextLevelDO.getCheckInDays()) {
                // 更新用户中的等级信息
                userDO.setLevelName(nextLevelDO.getLevelName());
                userDO.setLevelValue(nextLevelDO.getLevelValue());
                userDO.setLevelDate(LocalDate.now());
                userMapper.update(userDO, userDOLambdaUpdateWrapper);
            }
        } else if (nowLevelValue == 5) {
            // 特殊情况：如果当前等级为5，那么升级之后根据用户的连续签到天数，可能升到7/8/9级
            if (((checkInDays + days) >= nextLevelDO.getCheckInDays()) || (checkinStatisticsDO.getTotalDays() >= nextLevelDO.getCheckInDays())) {
                //if (checkinStatisticsDO.getTotalDays() >= nextLevelDO.getCheckInDays()) {
                // 当前最大连续签到的天数
                Integer curContinuousDays = checkinStatisticsDO.getCurContinuousDays();

                // 从7开始
                Integer level = nowLevelValue + 2;

                // 等级数据
                LevelDO levelestDO = null;

                // 查询条件


                while (true) {

                    LambdaQueryWrapper<LevelDO> levelestDOQueryWrapper = new LambdaQueryWrapper<>();
                    levelestDOQueryWrapper.eq(LevelDO::getLevelValue, level);
                    levelestDO = levelMapper.selectOne(levelestDOQueryWrapper);

                    // 不满足，跳出循环
                    if (curContinuousDays < levelestDO.getCheckInDays()) break;

                    // 满足，继续判断下一个等级
                    level += 1;

                }

                level -= 1;
                LambdaQueryWrapper<LevelDO> levelestDOQueryWrapper = new LambdaQueryWrapper<>();
                levelestDOQueryWrapper.eq(LevelDO::getLevelValue, level);
                levelestDO = levelMapper.selectOne(levelestDOQueryWrapper);

                // 更新用户中的等级信息
                userDO.setLevelName(levelestDO.getLevelName());
                userDO.setLevelValue(levelestDO.getLevelValue());
                userDO.setLevelDate(LocalDate.now());
                userMapper.update(userDO, userDOLambdaUpdateWrapper);
            }
        } else if (nowLevelValue == 6) { // 等级6 无论如何都是只根据当前最大连续签到天数有关
            if (checkinStatisticsDO.getCurContinuousDays() > nextLevelDO.getCheckInDays()) {
                // 更新用户中的等级信息
                userDO.setLevelName(nextLevelDO.getLevelName());
                userDO.setLevelValue(nextLevelDO.getLevelValue());
                userDO.setLevelDate(LocalDate.now());

                userMapper.update(userDO, userDOLambdaUpdateWrapper);
            }
        } else {
            LocalDate tomorrow = nowLevelDate.plusDays(1);
            // 当 当前最大连续签到起始日期 在 用户等级更新日期之后的一天 的后一天
            if (checkinStatisticsDO.getCurContinuousStartDate().isAfter(tomorrow)) {
                if (checkinStatisticsDO.getCurContinuousDays() > nextLevelDO.getCheckInDays()) {
                    // 更新用户中的等级信息
                    userDO.setLevelName(nextLevelDO.getLevelName());
                    userDO.setLevelValue(nextLevelDO.getLevelValue());
                    userDO.setLevelDate(LocalDate.now());
                    userMapper.update(userDO, userDOLambdaUpdateWrapper);
                }
            } else {
                if (nextLevelDO.getCheckInDays() == (checkInDays + days)) {
                    // 更新用户中的等级信息
                    userDO.setLevelName(nextLevelDO.getLevelName());
                    userDO.setLevelValue(nextLevelDO.getLevelValue());
                    userDO.setLevelDate(LocalDate.now());
                    userMapper.update(userDO, userDOLambdaUpdateWrapper);
                }
            }
        }
    }

    @Override
    public UserCheckinStatisticsDTO getUserCheckinStatistics(Long userId) {
        // 获取用户签到统计数据
        LambdaQueryWrapper<UserCheckinStatisticsDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserCheckinStatisticsDO::getUserId, userId);
        UserCheckinStatisticsDO checkinStatisticsDO = userCheckinStatisticsMapper.selectOne(queryWrapper);

        // 判断连续签到

        // 当前最大连续签到天数
        Integer curContinuousDays = checkinStatisticsDO.getCurContinuousDays();
        // 当前最大连续签到起始日期
        LocalDate curContinuousStartDate = checkinStatisticsDO.getCurContinuousStartDate();

        // 第一次注册账号时，参数为null,
        if (curContinuousStartDate == null) {
            checkinStatisticsDO.setTotalDays(0);
            checkinStatisticsDO.setMaxContinuousDays(0);
            checkinStatisticsDO.setCurContinuousDays(0);
        } else {
            // 最后连续签到日期
            LocalDate lastCheckinDate = curContinuousStartDate.plusDays(curContinuousDays - 1);
            // 当前日期
            LocalDate today = LocalDate.now();
            // 前一天日期
            LocalDate yesterday = today.minusDays(1);
            if (!(lastCheckinDate.isEqual(today) || lastCheckinDate.isEqual(yesterday))) {
                // 如果不是今天或者昨天，当前最大连续签到天数设为0
                checkinStatisticsDO.setCurContinuousDays(0);
            }
        }


        LambdaUpdateWrapper<UserCheckinStatisticsDO> userCheckinStatisticsDOLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userCheckinStatisticsDOLambdaUpdateWrapper.eq(UserCheckinStatisticsDO::getUserId, userId);
        userCheckinStatisticsMapper.update(checkinStatisticsDO, userCheckinStatisticsDOLambdaUpdateWrapper);


        UserCheckinStatisticsDTO userCheckinStatisticsDTO = reviewConverter.userCheckinStatisticsDO2DTO(checkinStatisticsDO);
        // 时间转换
        String strTotalTime = userCheckinStatisticsDTO.getTotalTime();
        Long LongTotalTime = Long.valueOf(strTotalTime);

        // 转换为分钟
        Long minute = LongTotalTime / 1000 / 60;

        userCheckinStatisticsDTO.setTotalTime(String.valueOf(minute));
        return userCheckinStatisticsDTO;
    }

    @Override
    public UserCheckinStatisticsDTO getUserContinuousCheckinInfo(Long userId) {
        return null;
    }

    @Override
    public void userResignCard(Long userId) {

        // 获取用户信息得到是否有补签卡
        LambdaQueryWrapper<UserDO> userDOQueryWrapper = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<UserDO> eq = userDOQueryWrapper.eq(UserDO::getId, userId);
        UserDO userDO = userMapper.selectOne(eq);

        // 获取当天的签到记录
        LambdaQueryWrapper<UserCheckinDO> checkinDOLambdaQueryWrapper = new LambdaQueryWrapper<>();
        checkinDOLambdaQueryWrapper.eq(UserCheckinDO::getUserId, userId)
                .eq(UserCheckinDO::getSignDate, LocalDate.now());
        UserCheckinDO todayUserCheckinDO = userCheckinMapper.selectOne(checkinDOLambdaQueryWrapper);
        // 拿最近的签到数据
//        QueryWrapper<UserCheckinDO> checkinDOQueryWrapper = new QueryWrapper<>();
//        checkinDOQueryWrapper.eq("user_id", userId)
//                .orderByDesc("sign_date")
//                .last("limit 1");
//        UserCheckinDO recentUserCheckinDO = userCheckinMapper.selectOne(checkinDOQueryWrapper);

        // 用户签到统计数据
        LambdaQueryWrapper<UserCheckinStatisticsDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserCheckinStatisticsDO::getUserId, userId);
        UserCheckinStatisticsDO userCheckinStatisticsDO = userCheckinStatisticsMapper.selectOne(lambdaQueryWrapper);

        // 没有补签卡，不允许补签
        if (userDO.getHasSignCard() == 0) {
            throw new BusinessException(ResultCodeEnum.NO_SIGN_CARD);
        }

        // 当前连续签到天数等于最大连续签到天数时，不能补签
        if (userCheckinStatisticsDO.getCurContinuousDays() == userCheckinStatisticsDO.getMaxContinuousDays()) {
            throw new BusinessException(ResultCodeEnum.ALREADT_MAX_CURRENTDAY);
        }

        // 如果没有签到记录，今天没有签到，不能使用补签卡
        if (todayUserCheckinDO == null) {
            throw new BusinessException(ResultCodeEnum.USER_CHECKIN_DATA_NEED_SYNC);
        }

        // 连续签到段号
        Long stickNo = todayUserCheckinDO.getStickNo();

        // 间隔天数
        int availableCheckinDays = userCheckinStatisticsDO.getMaxContinuousDays() - userCheckinStatisticsDO.getCurContinuousDays();
        LocalDate curContinuousStartDate = userCheckinStatisticsDO.getCurContinuousStartDate();

        // LocalDate endResignDate = curContinuousStartDate.minusDays(1);

        // 理想补签日期
        LocalDate possibleStartResignDate = curContinuousStartDate.minusDays(availableCheckinDays);

        // 拿理想补签日期的签到记录
        LambdaQueryWrapper<UserCheckinDO> possibleStartResignDateWrapper = new LambdaQueryWrapper<>();
        possibleStartResignDateWrapper.eq(UserCheckinDO::getSignDate, possibleStartResignDate)
                .eq(UserCheckinDO::getUserId, userId);
        UserCheckinDO possibleStartResignDateUserCheckinDO = userCheckinMapper.selectOne(possibleStartResignDateWrapper);

        // 理想补签日期的签到记录没有, 间隔日期里面的数据能覆盖
        if (possibleStartResignDateUserCheckinDO == null) {
            // 插入或更新间隔日期里面的数据
            insertOrUpdateUserCheckin(userId, possibleStartResignDate, stickNo, curContinuousStartDate);
        } else { // 理想补签日期有签到记录
            // 获取理想补签日期的最后连续签到日期
            QueryWrapper<UserCheckinDO> checkinDOQueryWrapper = new QueryWrapper<>();
            checkinDOQueryWrapper.eq("user_id", userId)
                    .eq("stick_no", possibleStartResignDateUserCheckinDO.getStickNo())
                    .orderByDesc("sign_date")
                    .last("limit 1");
            UserCheckinDO userCheckinDO = userCheckinMapper.selectOne(checkinDOQueryWrapper);

            // 更新理想补签日期
            possibleStartResignDate = userCheckinDO.getSignDate().plusDays(1);

            // 插入或更新间隔日期里面的数据
            insertOrUpdateUserCheckin(userId, possibleStartResignDate, stickNo, curContinuousStartDate);
        }

        // 间隔日期的天数，不包括结束日期当天
        long betweenDays = ChronoUnit.DAYS.between(possibleStartResignDate, curContinuousStartDate);

        // 更新用户签到统计数据
        userCheckinStatisticsDO.setTotalDays((int) (userCheckinStatisticsDO.getTotalDays() + betweenDays));
        userCheckinStatisticsDO.setCurContinuousStartDate(possibleStartResignDate);
        userCheckinStatisticsDO.setCurContinuousDays((int) (userCheckinStatisticsDO.getCurContinuousDays() + betweenDays));

        LambdaUpdateWrapper<UserCheckinStatisticsDO> userCheckinStatisticsDOLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userCheckinStatisticsDOLambdaUpdateWrapper.eq(UserCheckinStatisticsDO::getUserId, userId);
        userCheckinStatisticsMapper.update(userCheckinStatisticsDO, userCheckinStatisticsDOLambdaUpdateWrapper);

        // 用户等级更新
        userLevelUpdate(userId, userCheckinStatisticsDO);

        // 用户信息补签卡信息更新
        LambdaUpdateWrapper<UserDO> userDOLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userDOLambdaUpdateWrapper.eq(UserDO::getId, userId)
                .set(UserDO::getHasSignCard, 0);
        userMapper.update(userDOLambdaUpdateWrapper);
    }

    // 插入或更新间隔日期里面的数据
    private void insertOrUpdateUserCheckin(Long userId, LocalDate possibleStartResignDate, Long stickNo, LocalDate curContinuousStartDate) {
        while (true) {
            LambdaQueryWrapper<UserCheckinDO> resignDateWrapper = new LambdaQueryWrapper<>();
            resignDateWrapper.eq(UserCheckinDO::getUserId, userId)
                    .eq(UserCheckinDO::getSignDate, possibleStartResignDate);
            UserCheckinDO resignUserCheckinDO = userCheckinMapper.selectOne(resignDateWrapper);

            // 如果当天没有数据，插入
            if (resignUserCheckinDO == null) {
                UserCheckinDO userCheckinDO = new UserCheckinDO();
                userCheckinDO.setUserId(userId);
                userCheckinDO.setSignDate(possibleStartResignDate);
                userCheckinDO.setStickNo(stickNo);
                userCheckinDO.setType(2);
                userCheckinMapper.insert(userCheckinDO);

                // 更新单词上限和日志
                vocUpdateAndLog(userId);
            }

            // 有，更新
            LambdaUpdateWrapper<UserCheckinDO> userCheckinDOUpdateWrapper = new LambdaUpdateWrapper<>();
            userCheckinDOUpdateWrapper.eq(UserCheckinDO::getUserId, userId)
                    .eq(UserCheckinDO::getSignDate, possibleStartResignDate)
                    .set(UserCheckinDO::getStickNo, stickNo)
                    .set(UserCheckinDO::getType, 2);
            userCheckinMapper.update(userCheckinDOUpdateWrapper);

            // 下一天要更新或插入的日期
            LocalDate nextDate = possibleStartResignDate.plusDays(1);

            // 如果和起始日期匹配， 跳出循环
            if (nextDate.isEqual(curContinuousStartDate)) break;

            possibleStartResignDate = possibleStartResignDate.plusDays(1);
        }
    }
}
